/*->c.vax */


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <string.h>


#include "h.os"
#include "h.wimp"
#include "h.sprite"
#include "h.werr"
#include "h.wimpt"
#include "h.bbc"
#include "h.akbd"
#include "h.flex"

#include "h.def"

#include "h.wos"
#include "h.main"

#include "h.serial"
#include "h.mym"

#include "h.xext"


#include "h.vax"


/***************************************************************************/
/* Vasscom stuff */

/* #define DEBUG */

#ifdef DEBUG

FILE * vfp;

#endif




int vasscom=0; /* is vascomm up */   /* 0 == off 1== on 8==trickle   */
int trickle;                     




#define DLESTX 0x102
#define WACK   0x13B
#define ACK0   0x130
#define ACK1   0x131
#define DLEENQ 0x105
#define DLEETB 0x117
#define DLEITB 0x107
#define DLEETX 0x103
#define DLEEOT 0x104
#define DLESOH 0x101

#define CONTROL 1
#define TXBLOCK 2
#define RXBLOCK 2

#define SRXLEN 0x404
#define STXLEN 0x404
#define VRXLEN 0x404
#define VTXLEN 0x404

static char * vrxbf; /* these buffs are used by vax for its */
static char * vtxbf; /* operation */
static char * srxbf; /* these buffs are used by the system  */
static char * stxbf;


static int rxmode;    /* receive  mode 0=char 1=block */
static int txmode;    /* transmit mode 0=char 1=block */

static int rxstate;   /* machine states */
static int txstate;

static int rxtran;    /* rx transparent mode */
static int rxneg;     /* rx negotiate block */

static int negef;
static int negco;

static int rxresp;    /* time last byte was received */
static int txresp;    /* time we went into ackwait state */
static int txcount;   /* times to retx block */


static int vmici;
static int vtick;

static int txneg;     /* we have sent a negotiate block and are waiting for
                         a response */

static int rxdle;   /* true if the last rx'd byte was a DLE */
static int rxbcs;   /* 2,1,0 */
static int rxcon;   /* last received control code */
static int txcon;   /* last transmitted control code */

static int ackwait; /* waiting for an ACK */
static int lastack; /* last ack received */

static int nakwait; /* an ITB was naked so chuck new data */

static int vrxpoi;  /* vasscom rx buff pointer */

static int vtxpoi;  /* vasscom tx buff pointer */
static int vtxlen;

static int srxlo;   /* system rx buff pointers */
static int srxhi;

static int stxlo;   /* system tx buff pointers */
static int stxhi;

static int vablock;    /* vasscom block size 0,1,2,3,4,5 */

       int srxm=0;    /* set up rx mode */
       int stxm=0;    /* set up tx mode */
       int smbl=0;    /* set up block length */

static int altack;  /* alternating ACK 0 or 1 */

#define RXCOMLEN 16
#define TXCOMLEN 16

static int txcombf[TXCOMLEN];  /* buffer of commands to transmit */
static int rxcombf[RXCOMLEN];  /* buffer of commands received */

static int txcomlo;
static int txcomhi;
static int rxcomlo;
static int rxcomhi;


static int getvbyte(void);
static int getsbyte(void);
static void outv(int);
static void putsrx(int);
static void negotiate(void);


/*****************************************************************************/


static void swtxmo(int value)
{
 txmode=value;
}

static void swrxmo(int value)
{
 rxmode=value;
}

static void swbl(int value)
{
 vablock=value;
}


/****************************************************************************/



static int getrxcom(void)
{
 if(rxcomlo!=rxcomhi) 
      {
       if(++rxcomlo==RXCOMLEN) rxcomlo=0; return(rxcombf[rxcomlo]);
      }
 else 
       return(-1);
}


static int gettxcom(void)
{
 if(txcomlo!=txcomhi) 
      {
       if(++txcomlo==RXCOMLEN) txcomlo=0; return(txcombf[txcomlo]);
      }
 else 
       return(-1);

}


static void puttxcom(comm) int comm;
{
  if(++txcomhi==TXCOMLEN) txcomhi=0;
  txcombf[txcomhi]=comm;
}


static void putrxcom(comm) int comm;
{
  if(++rxcomhi==RXCOMLEN) rxcomhi=0;
  rxcombf[rxcomhi]=comm;
}


/****************************************************************************/

static int rxcrc;
static int txcrc;


static int crc_update(crc,crc_char) int crc; char crc_char; 
{ 
  int  i; 
  for(i = 0;i < 8;i++) 
  { 
    if((crc ^ crc_char) & 0x1) crc ^=0x14002;
    crc = crc >> 1;
    crc_char=crc_char >>1;
  }                                      
   return(crc);
} 


/****************************************************************************/


static void reset1(void)
{
 altack=ACK1;
 lastack=0;
 rxtran=0;
 txstate=CONTROL;
 rxstate=CONTROL;

 rxdle=0;
 rxbcs=0;
 ackwait=0;
 nakwait=0;

 txcomlo=txcomhi=0;
 rxcomlo=rxcomhi=0;
}


static void reset2(void)
{
 reset1();
 vtxpoi=vtxlen=0;
 stxlo=stxhi=0;
 vrxpoi=0;
 srxlo=srxhi=0;
 rxneg=txneg=0;

 rxcon=0;
 txcon=0;
}



static void reset3(void)
{
 reset2();
 swtxmo(0);
 swrxmo(0);
 swbl(0);
 negef=-1;
 vmici=0;
}



/************************************************************************/


static void sendblo(void)      /* send a data block from buffer */
{ int i;

  txcrc=0;
  vtxbf[0]=DLE;
  vtxbf[1]=STX;
  vtxlen=2;

  while(vtxlen<((1 << (vablock+5))-4))
   {
     i=getsbyte();
     if(i==-1) break;
     if(i==DLE) vtxbf[vtxlen++]=DLE;
                vtxbf[vtxlen++]=i;
                txcrc=crc_update(txcrc,i);
   }
   if(vtxlen==2) {vtxlen=0;return;}

   vtxbf[vtxlen++]=DLE;
   vtxbf[vtxlen++]=ETX;
   txcrc=crc_update(txcrc,ETX);

   vtxbf[vtxlen++]=txcrc;
   vtxbf[vtxlen++]=(txcrc >> 8);
}



static void sendneg(start,stxm,srxm,smbl) int start,stxm,srxm,smbl;
{
 int i;

 vtxbf[0]=SOH;
 vtxbf[1]=start;
 vtxbf[2]=0x30+srxm+(stxm << 1)+0xC;
 vtxbf[3]=0x51+smbl;
 vtxbf[4]=ETX;
 txcrc=0;
 for(i=1;i<5;i++) txcrc=crc_update(txcrc,vtxbf[i]);
 vtxbf[5]=txcrc;vtxbf[6]=(txcrc >> 8);
 vtxlen=7;

 txneg=1;
 vtxpoi=0;
 txstate=TXBLOCK;
}


void nego_blo(void)   /* we just got a negotiate block */
{
                       /* this is what the parameters are going to be; 
                                                              set up screen */
 swrxmo(vrxbf[1] & 0x1);
 swtxmo((vrxbf[1] & 0x2)>>1);

 if((vrxbf[2] & 0xF0)==0x40 || (vrxbf[2] & 0xF0)==0x50)
 {
  swbl((vrxbf[2] & 0xF)-1);
 }
 else
 {
  swbl(0);
 }


 if(vrxbf[0]==0x60)
 {                                  /* request block so have to respond */
  sendneg(0x61,srxm,stxm,smbl);
 }

 negef=-1;
}



void data_blo(void)   /* we just got some data */
{
 int i;
 for(i=0;i<vrxpoi-2;i++) putsrx(vrxbf[i]);
}




/***********************************************************************/




void txstatem(void)   /* transmit machine */
{ int comm;

if(txmode || txneg)
 switch(txstate)
  {
   case CONTROL:
                comm=getrxcom();
                switch(comm)
                 {
                   case DLEENQ:
                   case  ENQ: puttxcom(txcon);
                              break;
                   case WACK:
                              puttxcom(ENQ);
                              break;
                   case  ACK:
                   case ACK1:
                   case ACK0:
                              if(ackwait)
                              {
                                if(lastack==comm && (comm==ACK0 || comm==ACK1))
                                  {
                                    vtxpoi=0;
                                    txstate=TXBLOCK;
                                    break;
                                  }
                              }
                              lastack=comm;
                              if(txneg) txneg=0;
                              ackwait=0;
                              break;
                   case  NAK: 
                              /* ackwait=0; */
                              vtxpoi=0;
                              txstate=TXBLOCK;
                              break;
                 }

                if(ackwait && ((clock()-txresp)>250))
                   {
                  /*   if(serror()) {vascinit();return;} */


                     if((txcount-->0) && !txneg) {
                                                   puttxcom(ENQ);
                                                   txresp=clock();
                                                 }
                     else                      {txneg=0;ackwait=0;}
                   }

                comm=gettxcom();
                if(comm==-1) 
                           {
                            if(!txmode) break;
                            if(ackwait) break;
                            sendblo();
                            if(!vtxlen) break;
                            vtxpoi=0;
                            txstate=TXBLOCK;
                            txcount=7;
                            break;
                           }

                txcon=comm;
                if(comm & 0x100) outbytelo(DLE);
                if(comm!=0)      outbytelo(comm & 0xFF);
                break;

   case TXBLOCK:
                if(vtxpoi<vtxlen) outbytelo(vtxbf[vtxpoi++]);
                if(vtxpoi>=vtxlen) 
                 {
                  txstate=CONTROL;
                  ackwait=1;
                  txresp=clock();
                 }
                break;

  }

else
   {
     comm=getrxcom();
       if(comm==ENQ) puttxcom(txcon);

     comm=gettxcom();
        if(comm==-1) 
           {
             comm=getsbyte();
             if(comm!=-1) outbytelo(comm);
             return;
           }

     txcon=comm;
     if(comm & 0x100) outbytelo(DLE);
     if(comm!=0)      outbytelo(comm & 0xFF);

   }
}







/****************************************************************************/



void rxbcsm(byte) int byte;
{
    vrxbf[vrxpoi]=byte;             
    if(++vrxpoi==VRXLEN) vrxpoi--;

    if(rxbcs==2) {rxbcs=1;return;}
    rxbcs=0;

    if( ((rxcrc & 0xFF)!=vrxbf[vrxpoi-2]) || 
                        (((rxcrc >> 8) & 0xFF)!=vrxbf[vrxpoi-1]) || nakwait)
          {
           puttxcom(NAK);
           if(rxcon==ITB || rxcon==DLEITB) nakwait=1;

#ifdef DEBUG 
           vdu(7);
           fprintf(vfp,"NAK");
#endif

          }

   else  if(rxcon==DLEITB || rxcon==ITB)
          {
           puttxcom(ACK);
          }

    else
          {
            puttxcom(altack);
            altack ^=1;
          }

    /* was this data; yes then transfer it */

if(!nakwait)     /* not waiting for a pending nak */
 {
  if(rxneg)  nego_blo(); /* negotiate block */ 
  else       data_blo(); /* data block */      
 }

  if(rxcon==ITB || rxcon==DLEITB)
       {
         vrxpoi=0;
         rxtran=0;
         rxcrc=0;
       }

  else { /* going into RXSTATE */
         rxstate=CONTROL;
       }

}





void rxstatem(byte) int byte;  /* receive machine */
{

 if(byte==-1) 
     {
       if(rxstate==RXBLOCK && (clock()-rxresp)>300)
        {
          rxresp=clock();

       /*   if(serror()) {vascinit();return;}  */

          puttxcom(NAK);
          rxstate=CONTROL;
          rxbcs=0;
        }
       return;
     }

 else rxresp=clock();


#ifdef DEBUG

 putc(byte,vfp);

 vdu(4);tab(0,0);printf("rxbcs=%d rxdle=%d rxstate=%d txcon=%d rxcon=%d rxtran=%d",rxbcs,rxdle,rxstate,txcon,rxcon,rxtran);vdu(5);

#endif

 if(rxbcs) {rxbcsm(byte);return;}

 if(byte==DLE && !rxdle) {rxdle=1;return;}

 else {   /* sort out DLE sequences */
       if(rxdle)
        {
         rxdle=0;
         switch(byte)
          {
           case '0':
           case '1':
           case STX:
           case ETB:
           case ETX:
           case ENQ:
           case ITB:
           case EOT: byte+=0x100;
                     break;
                    /* note this lets DLE DLE = DLE */
          }
        }
      }

#ifdef DEBUG

vdu(4);tab(0,1);printf("byte=%d",byte);vdu(5);

#endif

 if(rxstate==CONTROL || (!rxmode && rxstate!=RXBLOCK))
    switch(byte)
      {
        case SOH:
                  rxneg=1;
                  vrxpoi=0;
                  rxcrc=0;
                  nakwait=0;
                  rxstate=RXBLOCK;
                  rxtran=0;
                  break;
        case STX:
                  rxneg=0;
                  vrxpoi=0;
                  rxcrc=0;
                  nakwait=0;
                  rxstate=RXBLOCK;
                  rxtran=0;
                  break;
        case DLESTX:
                  rxneg=0;
                  vrxpoi=0;
                  rxcrc=0;
                  nakwait=0;
                  rxstate=RXBLOCK;
                  rxtran=1;
                  break;
        case DLEEOT:
        case EOT:
                  rxstate=CONTROL;
                  altack=ACK1;
                  break;

        case DLEENQ:
        case  ENQ:
        case  ACK:
        case ACK0:
        case ACK1:
        case  NAK:
        case WACK:
                  putrxcom(byte);
                  break;
          default:if(!rxmode) putsrx(byte);
                  break;
   
   }

else
if(rxstate==RXBLOCK)
 {
  if(!rxtran)
    switch(byte)
       {
        case ETX:
        case ETB:
        case ITB:
                  rxcrc=crc_update(rxcrc,byte);
                  rxcon=byte;
                  rxbcs=2;
                  return;

        case EOT:
                  rxstate=CONTROL;
                  altack=ACK1;
                  return;

        case SOH: rxneg=1;
                  vrxpoi=0;
                  rxcrc=0;
                  rxstate=RXBLOCK;
                  rxtran=0;
                  return;

        case STX:
                  rxneg=0;
                  vrxpoi=0;

                  rxcrc=crc_update(0,STX);

                  rxstate=RXBLOCK;
                  rxtran=0;
                  return;

        case ENQ:             /* forward abort */
                  puttxcom(NAK);      
                  rxstate=CONTROL;
                  return;

       }
                      /* do these whether transparent or not */

    switch(byte)
       {
        case DLEETX:
        case DLEETB:
        case DLEITB:
                     rxcrc=crc_update(rxcrc,byte & 0xFF);
                     rxcon=byte;
                     rxbcs=2;
                     rxtran=0;
                     return;

        case DLEEOT:
                     rxstate=CONTROL;
                     altack=ACK1;
                     return;

        case DLESTX:
                     /* but if in RXBLOCK state do spec. thing with BCS */

                     rxcrc=crc_update(0,DLE);
                     rxcrc=crc_update(rxcrc,STX);

                     rxneg=0;
                     vrxpoi=0;
                     rxstate=RXBLOCK;
                     rxtran=1;
                     return;

        case DLEENQ:              /* forward abort */
                     puttxcom(NAK);
                     rxstate=CONTROL;
                     return;
       }

    /* not a control code so stash it */
   
    vrxbf[vrxpoi]=byte;               /* don't overwrite */
    if(++vrxpoi==VRXLEN) vrxpoi--;
    rxcrc=crc_update(rxcrc,byte);

 }
}



/****************************************************************************/

static int recp;

void vaxzero(void)     /* this routine is called at frequent intervals */
{
 if(recp) return;      /* it is non recursive */
 recp=1;
 if(vasscom)
 {
  rxstatem(getbytelo());
  txstatem();
  if(negef>=0)
  {
   if((clock()-negef)>500) 
   {
    if(--negco<0) reset3();
    else 
    {
     negef=clock();
     negotiate();
    }
   }
  }
 }
 recp=0;
}


/****************************************************************************/


/* get a byte from the vascomm input buffer */

static int getvbyte(void)
{
 if(srxlo!=srxhi) {if(++srxlo==SRXLEN) srxlo=0; return(srxbf[srxlo]);}
 else {vmici=0;return(-1);}
}



/* get a byte from the system output buffer */

static int getsbyte(void)
{
 if(stxlo!=stxhi) {if(++stxlo==STXLEN) stxlo=0; return(stxbf[stxlo]);}
 else return(-1);
}



/* put a byte into the system output buffer */

static void outv(int byte)
{
 if(++stxhi==STXLEN) stxhi=0;

 if(stxhi==stxlo)
 {
  while(stxhi==stxlo)
  {  
   poll(0);
/*   if(serror()) return;  */
  }
 }

 stxbf[stxhi]=byte;
}




 /* put a byte into the system input buffer*/

static void putsrx(int byte) 
{
 if(++srxhi==SRXLEN) srxhi=0;
 srxbf[srxhi]=byte;
}




/*****************************************************************************/

/* serial interface, getting and putting bytes */

/* cps table */
/*
static int vmic[15]={5,8,12,15,16,34,66,132,200,264,400,528,800,1056,2112};
 */

int  vaxgetbyte(void)
{
 int c;

 if(trickle)
 {
  if(clock()!=vtick)
  {
   vtick=clock();
   vmici+=rxbyterate;        /* vmic[rxrate]; */
  }
  if(vmici>=100)
  {
   vmici-=100;
   c=getvbyte();
  }
  else c=-1;
 }
 else c=getvbyte();

 return(c);
}



void vaxoutbyte(int byte)
{
 outv(byte);
}



/****************************************************************************/
/* entry points to outside world */

/* go into negotiate mode */

void negmode(void)
{
 reset3();
 negef=0;
 negco=4;
}


void xnegmode(int fp)
{
 negmode();
 fp=0;
}


/* initialise protocol */
/* initialise vasscom */

void vascinit(void)
{
 reset3();
}



/* stops vasscom */

static void susspend(void)
{
 int time;

 if(vasscom && (rxmode || txmode))
 {
  sendneg(0x60,0,0,smbl);      /* pause for 1 second */
  time=clock()+300;
  negef=-2;
  while(clock()<time && negef!=-1) poll(0); /* && txstate=TXBLOCK */
 }

}


void vasscomon(void)
{
 if(!vasscom)
 {
  flex_alloc((flex_ptr)&vrxbf,0x408);
  flex_alloc((flex_ptr)&vtxbf,0x408);
  flex_alloc((flex_ptr)&srxbf,0x408);
  flex_alloc((flex_ptr)&stxbf,0x408);
  addzeroevent(VAXZERO);
 }

 reset3();
 vasscom=1;
}


void vasscomoff(void)
{
 if(vasscom)
 {
  susspend();
  flex_free((flex_ptr)&vrxbf);
  flex_free((flex_ptr)&vtxbf);
  flex_free((flex_ptr)&srxbf);
  flex_free((flex_ptr)&stxbf);
  remzeroevent(VAXZERO);
 }

 reset3();
 vasscom=0;
}


void setvasscom(int on)
{
 if(on) vasscomon();
 else   vasscomoff();
}



static void negotiate(void)                 /* try to negotiate protocol */
{
 if(vasscom) sendneg(0x60,stxm,srxm,smbl);   /* send negotiate block */
}



/****************************************************************************/
                         /* VASSCOM menus */


int linktype;     /* 0==none, 1==Vascomm, 2==MNP */
int inlinktype;   /* internal link type          */


int internallink(int type)
{
 if(type!=inlinktype) setvasscom(type==1);
 inlinktype=type;
 return(type);
}


void driversetlink(int type)
{
 if(!xexec("modem_link",NULL,&type,&linktype)) linktype=internallink(type);
}


#ifdef NEVER

void setpoplink(void)
{
 int i;

 tickst(link_menu,0,linktype==0);
 tickst(link_menu,1,linktype==1);
 tickst(link_menu,2,linktype==2);
 tickst(link_menu,3,trickle);

 tickst(vax_menu,0,srxm);
 tickst(vax_menu,1,stxm);
 for(i=0;i<6;i++) tickst(vaxblksize_menu,i,smbl==i);
}



void decodevasscom(int m2,int m3,int m4)
{
 switch(m2)
 {
  case 0:        /* TX block */      /* opposite way round to menus */
         srxm^=1;
         break;

  case 1:        /* RX block */
         stxm^=1;
         break;

  case 2:        /* block size */
         if(m3>=0 && m3<=5) smbl=m3;
         break;

  case 3:        /* negotiate */

         break;
 }

 m4=0;
}




void decodelink(int m1,int m2,int m3,int m4)
{
 switch(m1)
 {
  case 0:
         driversetlink(0);
         break;

  case 1:                                        /* vasscom on/off */
         if(m2==-1) driversetlink(1);
         else       decodevasscom(m2,m3,m4);
         break;

  case 2:                                        /*   MNP          */
         driversetlink(2);
         break;

  case 3:
         trickle^=1;
         break;
 }
}

#endif




static int templinktype;
static int tempsmbl;
static int tempsrxm;
static int tempstxm;
static int temptrickle;


void linklevelicon(void)
{
 int handle=whandle[TLINKLEVEL];

 switch(icon)
 {
  case 0:
  case 1:
  case 2:
         if(icon!=templinktype)
         {
          deselect(handle,templinktype);
          templinktype=icon;
          select(handle,templinktype);
         }
         break;

  case 3:
         selectst(handle,3,temptrickle^=1);
         break;

  case 6:
         selectst(handle,6,tempsrxm^=1);
         break;

  case 7:
         selectst(handle,7,tempstxm^=1);
         break;

 case  9:
 case 11:
         if(buttons==0x1) icon=20-icon;
         if(icon==9 && tempsmbl>0) tempsmbl--;
         else
         if(icon==11 && tempsmbl<5) tempsmbl++;
         else return;
         writeiconf(handle,10,"%d",1<<(tempsmbl+5));
         break;

  case 13:
          driversetlink(templinktype);
          smbl=tempsmbl;
          srxm=tempsrxm;
          stxm=tempstxm;
          trickle=temptrickle;

          if(buttons==0x4) zapmenu();
          break;

  case 12:
          negotiate();
          break;
 }
}


int setuplinklevel(void)
{
 int handle=createwindow(TLINKLEVEL);

 templinktype=linktype;
 tempsmbl=smbl;
 tempsrxm=srxm;
 tempstxm=stxm;
 temptrickle=trickle;

 selectst(handle,0,templinktype==0);
 selectst(handle,1,templinktype==1);
 selectst(handle,2,templinktype==2);
 selectst(handle,3,temptrickle);
 selectst(handle,6,tempsrxm);
 selectst(handle,7,tempstxm);
 writeiconf(handle,10,"%d",1<<(tempsmbl+5));

 return(handle);
}

